package com.rainripple.java.common.mapper;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;

import com.rainripple.java.common.log.Log;
import com.rainripple.java.common.util.StringUtil;

public class EntityMapper {

	private  String path = "classpath*:com/rainripple/java/starter/*/entity/*.class";
	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	private static final ResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver();
	private static final MetadataReaderFactory READER_FACTORY = new SimpleMetadataReaderFactory();
	private static final Map<String, EntityInfo> ENTITY = new HashMap<String, EntityInfo>();
	private static final Map<String, byte[]> MAPPER = new HashMap<String, byte[]>();

	public EntityMapper() {
		loadEntity();
		buildMapper();
	}

	public void addMapper(Configuration configuration) throws IOException {
		try {
			for (Map.Entry<String, byte[]> entity : MAPPER.entrySet()) {
				byte[] mapper = entity.getValue();
				InputStream mapperInputStream = new ByteArrayInputStream(mapper);
				XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperInputStream, configuration, entity.getKey(), configuration.getSqlFragments());
				xmlMapperBuilder.parse();
			}

		} catch (Exception e) {
			throw new NestedIOException("Failed to parse mapping resource: '" + "" + "'", e);
		} finally {
			ErrorContext.instance().reset();
		}
	}

	private String buildMapper() {
		IMapperBuilder iMapperBuilder = new FreeMarkerMapperBuilder();
	//	 IMapperBuilder iMapperBuilder = new StringMapperBulider();
		for (Map.Entry<String, EntityInfo> entity : ENTITY.entrySet()) {
			byte[] mapper = iMapperBuilder.build(entity.getValue());
			MAPPER.put(entity.getKey(), mapper);
			Log.debug(new String(mapper));
		}
		return null;
	}

	private void loadEntity() {
		Resource[] resources;
		try {
			resources = RESOLVER.getResources(path);
			for (Resource res : resources) {
				MetadataReader meta = READER_FACTORY.getMetadataReader(res);
				Class<?> cls = Class.forName(meta.getClassMetadata().getClassName());
				checkEntity(cls);
			}
		} catch (IOException e1) {
			// TODO
		} catch (ClassNotFoundException e) {
			// TODO
		}

	}

	private void checkEntity(Class<?> cls) {
		if (cls.isAnnotationPresent(Mapper.class) && cls.isAnnotationPresent(Entity.class)) {
			String table = null;
			if (cls.isAnnotationPresent(Table.class)) {
				table = cls.getAnnotation(Table.class).name().toLowerCase();
			}
			if (StringUtil.isNullOrEmpty(table)) {
				table = cls.getSimpleName().toLowerCase();
			}
			EntityInfo entityInfo = new EntityInfo();
			entityInfo.setName(cls.getName());
			entityInfo.setSimpleName(cls.getSimpleName());
			entityInfo.setTable(table);
			String packageName = cls.getPackage().getName();
			entityInfo.setBasePackage(packageName.substring(0, packageName.lastIndexOf(".")));
			ENTITY.put(cls.getName(), entityInfo);
			for (; !cls.equals(Object.class); cls = cls.getSuperclass()) {
				Field[] fields = cls.getDeclaredFields();
				for (Field field : fields) {
					String column = null;
					if (field.isAnnotationPresent(Column.class)) {
						column = field.getAnnotation(Column.class).name().toLowerCase();
					} else {
						column = getColumnName(field);
					}
					FieldInfo fieldInfo = new FieldInfo();
					fieldInfo.setColumn(column);
					fieldInfo.setName(field.getName());
					fieldInfo.setType(field.getType().getName());
					if (field.isAnnotationPresent(Id.class)) {
						List<FieldInfo> keyFieldInfos = entityInfo.getKeyFieldInfos();
						if (null == keyFieldInfos) {
							keyFieldInfos = new ArrayList<FieldInfo>();
							entityInfo.setKeyFieldInfos(keyFieldInfos);
						}
						keyFieldInfos.add(fieldInfo);
					} else {
						List<FieldInfo> colFieldInfos = entityInfo.getColFieldInfos();
						if (null == colFieldInfos) {
							colFieldInfos = new ArrayList<FieldInfo>();
							entityInfo.setColFieldInfos(colFieldInfos);
						}
						colFieldInfos.add(fieldInfo);
					}
				}
			}

		}
	}
	
	private String getColumnName(Field field) {
		String name = field.getName();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < name.length(); i++) {
			char letter = name.charAt(i);
			if (letter >= 'A' && letter <= 'Z') {
				sb.append('_').append(letter);
			} else {
				sb.append(letter);
			}
		}
		return sb.toString().toLowerCase();
	}
}
